作者:陈广
日期:2018-2-15
上一篇文章简单演示了委托、匿名函数、Lambda表达式。仅仅这样肯定是不够的,还需要更多了解它们的示例,才能在阅读相关代码时不会那么吃力。所以这篇文章就举一些例子帮助读者作进一步的了解。将来学习.NET Core时遇到典型应用场景时,也会追加到这篇文章内。
List<T>.ForEach
先看例子:
using System;
using System.Collections.Generic;
namespace lambda
{
class Program
{
static void Main(string[] args)
{
List<int> nums = new List<int>() { 1, 2, 3, 4, 5, 6 };
nums.ForEach(number => Console.Write(number + " "));
}
}
}
运行结果:1 2 3 4 5 6
在此例中,我们先声明了一个泛型集合List,指定了其中只能装载整数类型,然后将1到6装入此集合。接下来,调用List<T>
的ForEach
方法遍历所有元素并打印。
此例关键为ForEach
是如何动作的。先来看ForEach
函数原型:
public void ForEach(Action<T> action)
ForEach
函数需传递一个Action<T>
参数,上篇文章中我们已经知道,Action<T>
为带参无返回值委托。它的作用是提供如何处理List<T>
集合中的每一个元素的方法。此例中,我们使用的委托为:
number => Console.Write(number + " ")
它只是简单地打印出当前元素,并在其后添加一个空格。
当然,此时我们无法体会到使用委托的优势,但当有多个处理方法,或者需要将此动作作为参数进行传递时,就能体现优势了。修改程序:
static Action<int> print1 = number => Console.Write(number + " ");
static Action<int> print2 = number => Console.Write(number + "-");
static void Main(string[] args)
{
List<int> nums = new List<int>() { 1, 2, 3, 4, 5, 6 };
nums.ForEach(print1);
Console.WriteLine();
nums.ForEach(print2);
}
运行结果:
1 2 3 4 5 6
1-2-3-4-5-6-
假设针对这个List<T>
里的元素有多种处理方法,那么我们只需针对每一种处理方法声明一个委托。在调用时根据你需要的处理方法调用相应委托即可。此例我们为简单起见,只是打印每个元素,第一种处理方法是使用空格分隔,第二种处理方法是使用减号分隔。最后两种方式都打印出来。
List<T>
里的元素我们称为内容,而处理方法Action<T>
称为动作,ForEach()
方法则使得我们有机会将内容和动作分离。
Comparison<T>
下面我们把上面的例子稍作修改,使用List<T>
的``sort()`方法进行排序:
static Action<int> print = number => Console.Write(number + " ");
static void Main(string[] args)
{
List<int> nums = new List<int>() { 3, 1, 6, 5, 2, 4 };
nums.Sort();
nums.ForEach(print);
}
运行结果:1 2 3 4 5 6
,
这样排序不会有问题,但如果List<T>
里存放的是一个自定义对象就不一定了。我们修改程序,在List<T>
内存放自定义对象:
class student
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
static Action<student> print = stu => Console.WriteLine($"学号={stu.ID},姓名={stu.Name},出生日期={stu.Birthday}");
static void Main(string[] args)
{
List<student> stus = new List<student>
{
new student{ID=3,Name="张三",Birthday=DateTime.Parse("1990-5-1") },
new student{ID=2,Name="李四",Birthday=DateTime.Parse("1990-1-1") },
new student{ID=4,Name="王五",Birthday=DateTime.Parse("1990-2-1") },
new student{ID=1,Name="马六",Birthday=DateTime.Parse("1990-9-1") }
};
stus.Sort()
stus.ForEach(print);
}
运行程序,抛出异常。这是因为Sort()
方法需要有两个元素对比的规则。之前集合内存放的是整数,整数本身是已经有对比规则的,但我们自定义的类并无实现对比规则,所以在调用Sort()
时出错,它不知道如何对比两个元素。
在Sort()
方法中加入对比规则有三种方式:
student
类实现IComparable<T>
接口IComparer
或IComparer<T>
接口,作为参数传递给Sort()
方法作为比较依据Comparison<T>
委托,作为参数传递给Sort()
方法作为比较依据前两种方法不在本文讲述范围,第二种方法在我的《数据结构 C#语言描述》中有相关例子。我们这里只讲解第三种方法的实现。
Comparison
和Action
一样,都是微软内置的委托对象。我们先到MSDN中查看Comparison<T>
委托原型:
public delegate int Comparison<in T>(T x, T y);
T
:需要进行对比的对象的类型x
:对比的第一个对象y
:对比的第二个对象下面更改之前的程序,使之正常运行:
class student
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
static Action<student> print = stu => Console.WriteLine($"学号={stu.ID},姓名={stu.Name},出生日期={stu.Birthday}");
//使用ID进行排序的委托
static Comparison<student> comparyByID = (stu1, stu2) => stu1.ID.CompareTo(stu2.ID);
//使用Name进行排序的委托
static Comparison<student> comparyByName = (stu1, stu2) => stu1.Name.CompareTo(stu2.Name);
//使用Birthday进行排序的委托
static Comparison<student> comparyByBirthday = (stu1, stu2) => stu1.Birthday.CompareTo(stu2.Birthday);
static void Main(string[] args)
{
List<student> stus = new List<student>
{
new student{ID=3,Name="张三",Birthday=DateTime.Parse("1990-5-1") },
new student{ID=2,Name="李四",Birthday=DateTime.Parse("1990-1-1") },
new student{ID=4,Name="王五",Birthday=DateTime.Parse("1990-2-1") },
new student{ID=1,Name="马六",Birthday=DateTime.Parse("1990-9-1") }
};
stus.Sort(comparyByID); //使用ID进行排序
stus.ForEach(print);
Console.WriteLine("--------------------------------------------");
stus.Sort(comparyByName);//使用Name进行排序
stus.ForEach(print);
Console.WriteLine("--------------------------------------------");
stus.Sort(comparyByBirthday);//使用Birthday进行排序
stus.ForEach(print);
Console.ReadLine();
}
运行结果:
学号=1,姓名=马六,出生日期=1990/9/1 0:00:00
学号=2,姓名=李四,出生日期=1990/1/1 0:00:00
学号=3,姓名=张三,出生日期=1990/5/1 0:00:00
学号=4,姓名=王五,出生日期=1990/2/1 0:00:00
--------------------------------------------
学号=2,姓名=李四,出生日期=1990/1/1 0:00:00
学号=1,姓名=马六,出生日期=1990/9/1 0:00:00
学号=4,姓名=王五,出生日期=1990/2/1 0:00:00
学号=3,姓名=张三,出生日期=1990/5/1 0:00:00
--------------------------------------------
学号=2,姓名=李四,出生日期=1990/1/1 0:00:00
学号=4,姓名=王五,出生日期=1990/2/1 0:00:00
学号=3,姓名=张三,出生日期=1990/5/1 0:00:00
学号=1,姓名=马六,出生日期=1990/9/1 0:00:00
这里,我们通过在Sort()
中使用不同的委托,可以实现按照不同的字段进行排序,从而得到不同的排序结果。